package com.bhudy.base;

import com.bhudy.enums.Relation;
import com.bhudy.exception.SysException;
import com.bhudy.exception.SysExceptionCode;
import com.bhudy.utils.XqhUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author bhudy
 * @Description 查询条件
 * @Date 2019-10-30 23:09:37
 */
public class QueryFilter {

    class WHERE {
        public WHERE(Relation relation1, String name, Relation relation2, Object... args) {
            if (whereList.size() == 0) {
                relation1 = Relation.WHERE;
            }
            this.relation1 = relation1;
            this.name = name;
            this.relation2 = relation2;
            this.args = args;
            this.type = 0;
        }

        public WHERE(String sql, Object... args) {
            this.sql = sql;
            this.args = args;
            this.type = 1;
        }

        private int type = 0;
        private Relation relation1;
        private String name;
        private Relation relation2;
        private Object[] args;
        private String sql;

        public int getType() {
            return type;
        }

        public void setType(int type) {
            this.type = type;
        }

        public Relation getRelation1() {
            return relation1;
        }

        public void setRelation1(Relation relation1) {
            this.relation1 = relation1;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Relation getRelation2() {
            return relation2;
        }

        public void setRelation2(Relation relation2) {
            this.relation2 = relation2;
        }

        public Object[] getArgs() {
            return args;
        }

        public void setArgs(Object[] args) {
            this.args = args;
        }

        public String getSql() {
            return sql;
        }

        public void setSql(String sql) {
            this.sql = sql;
        }
    }

    List<WHERE> whereList = new ArrayList<>(); // 查询条件
    private Map<String, Object> argMap = new HashMap<>(); // 参数Map
    Map<Integer, String> orderMap = new HashMap<>(); // 排序条件
    private List<String> argStrList = new ArrayList<>(); // 参数列表
    private String limit = ""; // 分页条件
    Integer[] limits = new Integer[2];

    public QueryFilter clear() {
        whereList.clear();// = new ArrayList<>();
        argMap.clear();// = new HashMap<>();
        orderMap.clear();// = new HashMap<>();
        argStrList.clear();// = new ArrayList<>();
        limit = "";
        limits = new Integer[2];
        return this;
    }


    public QueryFilter where(Relation relation1, String name, Relation relation2, Object... args) {
        whereList.add(new WHERE(relation1, name, relation2, args));
        return this;
    }

    public QueryFilter and(String name, Relation relation, Object... args) {
        return where(Relation.AND, name, relation, args);
    }

    public QueryFilter and(String name, Object... args) {
        return where(Relation.AND, name, Relation.EQUAL, args);
    }

    public QueryFilter andLike(String name, Object... args) {
        return and(name, Relation.LIKE, args);
    }

    public QueryFilter or(String name, Relation relation, Object... args) {
        return where(Relation.OR, name, relation, args);
    }

    public QueryFilter or(String name, Object... args) {
        return where(Relation.OR, name, Relation.EQUAL, args);
    }

    public QueryFilter orLike(String name, Object... args) {
        return or(name, Relation.LIKE, args);
    }

    public QueryFilter andBetween(String name, Object args1, Object args2) {
        return and(name, Relation.BETWEEN, args1, args2);
    }

    public QueryFilter orBetween(String name, Object args1, Object args2) {
        return or(name, Relation.BETWEEN, args1, args2);
    }

    public QueryFilter sql(String sql, Object... args) {
        whereList.add(new WHERE(sql, args));
        return this;
    }

    /**
     * 排序
     *
     * @param relation
     * @param names
     */
    public QueryFilter order(Relation relation, String... names) {
        if (relation != Relation.ASC && relation != Relation.DESC) {
            throw new RuntimeException("排序只能使用ASC或者DESC");
        }
        for (String name : names) {
            orderMap.put(orderMap.size(), name + "_!" + relation.getValue());
        }
        return this;
    }

    /**
     * 排序，升排
     *
     * @param names
     */
    public QueryFilter orderASC(String... names) {
        return order(Relation.ASC, names);
    }

    /**
     * 排序，降排
     *
     * @param names
     */
    public QueryFilter orderDESC(String... names) {
        return order(Relation.DESC, names);
    }

    /**
     * 限定查询结果返回的条数
     *
     * @param pageNo   第几页
     * @param pageSize 每页多少条
     */
    public QueryFilter limit(int pageNo, int pageSize) {
        // limit = Relation.LIMIT.getValue() + (pageNo * pageSize) + "," + pageSize;
        limit = "LIMIT " + pageSize + " OFFSET " + (pageNo * pageSize);
        limits[0] = pageSize;
        limits[1] = (pageNo * pageSize);
        return this;
    }

    public String getLimit() {
        return limit;
    }

    public StringBuffer getOrder() {
        StringBuffer orderSub = new StringBuffer();

        if (orderMap.size() != 0) {
            orderSub.append(" order by ");
            for (int i = 0; i < orderMap.size(); i++) {
                orderSub.append(orderMap.get(i) + ",");
            }
            orderSub.deleteCharAt(orderSub.length() - 1);
        }

        return orderSub;
    }

    public StringBuffer getWhere() {
        StringBuffer whereSb = new StringBuffer();
        if (whereList.size() != 0) {
            for (WHERE where : whereList) {
                Relation relation1 = where.relation1;
                String name = where.name;
                Relation relation2 = where.relation2;
                Object[] args = where.args;
                String sql = where.sql;

                if (where.type == 0) {
                    String[] names = name.split("\\.");
                    String nameFormat;
                    if (name.length() == 0) {
                        nameFormat = null;
                    } else if (names.length == 1) {
                        nameFormat = "`" + XqhUtils.toUpperCaseFirstOneAdd(name) + "`";
                    } else {
                        nameFormat = "`" + XqhUtils.toLowerCaseFirstOneDel(names[0]) + "`.`" + XqhUtils.toUpperCaseFirstOneAdd(names[1]) + "`";
                    }

                    if (relation2 == Relation.IN) {
                        if (args == null || args.length == 0) {
                            throw new SysException(SysExceptionCode.CODE_40003);
                        }
                        StringBuffer whereArg = new StringBuffer();
                        for (Object arg : args) {
                            whereArg.append("#{argMap.arg" + argMap.size() + "}, ");
                            argMap.put("arg" + argMap.size(), arg);
                        }

                        if (whereArg.length() != 0) {
                            whereArg.deleteCharAt(whereArg.length() - 2);
                            whereArg.insert(0, "(");
                            whereArg.append(")");
                        }
                        whereSb.append(relation1.getValue() + nameFormat + relation2.getValue() + whereArg);
                    } else if (relation2 == Relation.IS_NOT_NULL || relation2 == Relation.IS_NULL) {
                        whereSb.append(relation1.getValue() + nameFormat + relation2.getValue());
                    } else if (relation2 == Relation.BETWEEN) {
                        if (args == null || args.length == 0) {
                            throw new SysException(SysExceptionCode.CODE_40003);
                        } else if (args.length == 1) {
                            Object str = args[1];
                            args = new Object[2];
                            args[0] = str;
                        }
                        String str1 = "#{argMap.arg" + argMap.size() + "}";
                        argMap.put("arg" + argMap.size(), args[0]);
                        String str2 = "#{argMap.arg" + argMap.size() + "}";
                        argMap.put("arg" + argMap.size(), args[1]);

                        whereSb.append(relation1.getValue() + nameFormat + relation2.getValue() + str1 + Relation.AND.getValue() + str2);
                    } else {
                        if (args == null || args.length == 0) {
                            String str = "#{argMap.arg" + argMap.size() + "}";
                            argMap.put("arg" + argMap.size(), null);
                            whereSb.append(relation1.getValue() + nameFormat + relation2.getValue() + str);

                        } else {
                            for (Object arg : args) {
                                String str = "#{argMap.arg" + argMap.size() + "}";
                                argMap.put("arg" + argMap.size(), arg);
                                whereSb.append(relation1.getValue() + nameFormat + relation2.getValue() + str);
                            }
                        }
                    }
                } else {
                    whereSb.append(" " + XqhUtils.formatSql(sql, argMap.size()));
                    if (XqhUtils.isBlank(args)) {
                        for (Object arg : args) {
                            argMap.put("arg" + argMap.size(), arg);
                        }
                    }
                }
            }
        }
        return whereSb;
    }

    public Map<String, Object> getArgMap() {
        return argMap;
    }

    public List<String> getArgsStr() {
        return argStrList;
    }

    private QueryFilter addArgsStr(String args) {
        argStrList.add(args);
        return this;
    }

    public QueryFilter putArgs(Object args) {
        String sqlStr = new String();
        if (args == null) {
            throw new SysException(SysExceptionCode.CODE_40003);
        } else if (args.getClass().isArray()) {
            sqlStr += "(";
            for (Object arg : (Object[]) args) {
                sqlStr += "#{argMap.arg" + argMap.size() + "},";
                argMap.put("arg" + argMap.size(), arg);
            }
            sqlStr = sqlStr.substring(0, sqlStr.length() - 1) + ")";
        } else {
            sqlStr += "#{argMap.arg" + argMap.size() + "}";
            argMap.put("arg" + argMap.size(), args);
        }
        return addArgsStr(sqlStr);
    }
}